package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.common.to.HasStockTo;
import com.atguigu.common.to.mq.WareStockEdTo;
import com.atguigu.common.to.mq.WareTaskDetailTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.to.LockStockTo;
import com.atguigu.gulimall.ware.to.OrderItemVo;
import com.atguigu.gulimall.ware.to.OrderTo;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

@RabbitListener(queues = "stock.release.stock.queue")
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    private WareOrderTaskService wareOrderTaskService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    OrderFeignService orderFeignService;

    @RabbitHandler
    public void handle(WareStockEdTo wareStockEdTo, Channel channel, Message message) throws IOException {
        System.out.println("收到解锁库存消息");
        //判断库存工作单表是否有该记录
        Long taskId = wareStockEdTo.getTaskId();
        WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(taskId);
        WareTaskDetailTo detail = wareStockEdTo.getDetail();
        Long id = detail.getId();
        WareOrderTaskDetailEntity byId = wareOrderTaskDetailService.getById(id);
        if (byId == null) {
            //若没有，则说明库存锁定失败
            //无需处理，可能由于库存不足导致
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } else {
            //否则库存锁定成功，可以自动解锁库存
            //订单状态为取消或者订单不存在
            try {
                R r = orderFeignService.orderStatus(taskEntity.getOrderSn());
                if(r.getCode()==0){
                    //远程调用成功
                    OrderTo data = r.getData(new TypeReference<OrderTo>() {
                    });
                    if(data==null||data.getStatus()==4){
                        //订单已取消或者不存在 ，则解锁库存
                        //并且只能解锁锁定状态的库存
                        if(detail.getLockStatus()==1){
                            unLockStock(detail.getSkuId(),detail.getSkuNum(),detail.getWareId());
                            //解锁之后需要修改工作单状态为已解锁
                            wareOrderTaskDetailService.unlock(detail);
                            //手动确认消息
                            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                        }
                        else{
                            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                        }
                    }
                    else{
                        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                    }
                }
                else{
                    //失败，重试
                    channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);//拒绝消息并重新入队
                }
            }catch (Exception e){
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),true);//拒绝消息并重新入队
            }

        }

    }

    /**
     * 解锁库存
     * @param skuId 哪个商品
     * @param skuNum 数量
     * @param wareId 哪个仓库
     */
    private void unLockStock(Long skuId, Integer skuNum, Integer wareId) {
        this.baseMapper.unLockStock(skuId,skuNum,wareId);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                new QueryWrapper<WareSkuEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public void updateByPurchase(Long skuId, Long wareId, Integer skuNum) {
        //1.库存存在
        List<WareSkuEntity> ware = this.list(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (ware != null && ware.size() > 0) {
            baseMapper.updateBySkuAndWare(skuId, wareId, skuNum);
        } else {
            //2.不存在
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            wareSkuEntity.setStockLocked(50);
            //设置商品名称，出现异常则捕获
            try {
                R res = productFeignService.infobyfeign(skuId);
                if (res.getCode() == 0) {
                    Map<String, Object> map = (Map<String, Object>) res.get("skuInfo");
                    wareSkuEntity.setSkuName((String) map.get("skuName"));
                }
            } catch (Exception e) {

            }
            this.save(wareSkuEntity);
        }
    }

    @Override
    public List<HasStockTo> getHasStock(List<Long> skusIds) {
        List<HasStockTo> collect = skusIds.stream().map(sku -> {
            Integer stock = baseMapper.hasStock(sku);
            HasStockTo hasStock = new HasStockTo();
            hasStock.setStock(stock == null ? false : stock > 0);
            hasStock.setSkuId(sku);
            return hasStock;
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 锁定库存
     *
     * @param lockStockTo
     * @return
     */
    @Transactional(rollbackFor = NoStockException.class)
    @Override
    public Boolean lock(LockStockTo lockStockTo) {
        //保存工作单信息，用来回溯数据
        WareOrderTaskEntity taskEntity = new WareOrderTaskEntity();
        taskEntity.setOrderSn(lockStockTo.getOrderSn());
        wareOrderTaskService.save(taskEntity);

        //1.查询每个商品在哪些仓库有库存
        List<OrderItemVo> orderItems = lockStockTo.getOrderItems();
        List<SkuWareHasStock> collect = orderItems.stream().map(item -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            stock.setNum(item.getCount());
            stock.setSkuId(item.getSkuId());
            List<Long> wareIds = getWareBySkuId(item.getSkuId());
            stock.setWareId(wareIds);
            return stock;
        }).collect(Collectors.toList());
        //2.锁定库存
        Boolean allStocked = true;
        for (SkuWareHasStock stock : collect) {
            Boolean stocked = false;
            Long skuId = stock.getSkuId();
            List<Long> wareId = stock.getWareId();
            if (wareId == null || wareId.size() == 0) {
                //当前商品没有库存，直接抛异常退出
                throw new NoStockException(skuId);
            } else {
                for (Long ware : wareId) {
                    //锁库存
                    Long res = this.baseMapper.lockWareStock(skuId, ware, stock.getNum());
                    if (res == 1) {
                        //锁库存成功
                        stocked = true;
                        //保存工作单详情
                        WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
                        detailEntity.setSkuId(skuId);
                        detailEntity.setSkuNum(stock.getNum());
                        detailEntity.setLockStatus(1);
                        detailEntity.setTaskId(taskEntity.getId());
                        detailEntity.setWareId(ware.intValue());
                        wareOrderTaskDetailService.save(detailEntity);
                        //给mq发送消息
                        WareTaskDetailTo taskDetailTo = new WareTaskDetailTo();
                        BeanUtils.copyProperties(detailEntity, taskDetailTo);
                        WareStockEdTo wareStockEdTo = new WareStockEdTo();
                        wareStockEdTo.setTaskId(detailEntity.getTaskId());
                        wareStockEdTo.setDetail(taskDetailTo);

                        rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", wareStockEdTo);

                        break;
                    } else {
                        //该仓库没有库存，到下个仓库继续
                    }
                }
                if (!stocked) {
                    //该商品所有仓库都没锁住
                    throw new NoStockException(skuId);
                }
            }
        }
        return true;
    }

    @Override
    public void unlock(List<WareOrderTaskDetailEntity> taskDetailEntities) {
        for (WareOrderTaskDetailEntity detail : taskDetailEntities) {
            this.baseMapper.unLockStock(detail.getSkuId(),detail.getSkuNum(),detail.getWareId());
        }
    }

    private List<Long> getWareBySkuId(Long skuId) {
        return this.baseMapper.getWareBySkuId(skuId);
    }

    @Data
    class SkuWareHasStock {
        private Long skuId;
        private List<Long> wareId;
        private Integer num;
    }

}